#include <iostream>
#include <assert.h>
#include <vector>
#include <string>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "work.hpp"

using namespace std;
#define BUFFSIZE 1024
// a

const int pipe_num = 5;
// 判断是第几个管道
static int name_flag = 1;
class channel
{
public:
    channel(int fd, pid_t id)
        : _ctrlfd(fd), _workid(id)
    {
        _name = "channel-" + to_string(name_flag);
        name_flag++;
    }
    int GetFd() const
    {
        return _ctrlfd;
    }
    pid_t GetId() const
    {
        return _workid;
    }
    string GetName() const
    {
        return _name;
    }

private:
    int _ctrlfd;
    pid_t _workid;
    string _name;
};

void ChildWork()
{
    while (1)
    {
        int code = 0;
        // 子进程只读，当父进程没有写入指令时，子进程无法工作
        // 父进程写4个字节的数据 子进程读取4个字节
        ssize_t n = read(0, &code, sizeof(code));
        // 对应任务码

        if (n == sizeof(code))
        {
            // n值正常
            if (!init.CheckSafe(code))
                continue;
            init.RunTask(code);
        }
        else if (n == 0)
        {
            break;
        }
    }

    cout << "子进程已退出" << endl;
}

void CreatChannels(vector<channel> &channels)
{
    vector<int> fd_write;
    for (int i = 0; i < pipe_num; i++)
    {
        // 1.定义并创建管道
        int pipefd[2];
        int n = pipe(pipefd);
        cout << "成功创建管道：" << i << endl;

        assert(n == 0);

        // 2.创建进程
        pid_t id = fork();
        assert(id != -1);

        // 3.构建单向信道
        if (id == 0)
        {
            // 对于子进程来说 只要出现拷贝了父进程的写
            // 就需要进行关闭，才能实现单向传输的管道
            if (!fd_write.empty())
            {
                for (size_t j = 0; j < fd_write.size(); j++)
                {
                    // 关闭我们插入数组内容
                    close(fd_write[j]);
                    cout << "process: " << getpid() << " close: " << fd_write[j] << endl;
                }
            }

            // child
            // 子进程关闭当前的 写 端
            close(pipefd[1]);
            // 重定向到标准输入
            dup2(pipefd[0], 0);
            ChildWork();
            exit(0);
        }

        // father
        close(pipefd[0]);

        // 存储写对应的下标相对值
        fd_write.push_back(pipefd[1]);

        // 传入这个 写 对应的下标文件给channel
        channels.push_back(channel(pipefd[1], id));
        // 测试父进程的写文件
        // cout<< pipefd[1] <<endl;
    }
    cout << "管道已全部创建，开始执行任务" << endl;
}
void SendCommand(const vector<channel> &channels, int flag = -1)
{

    int position = 0;
    while (1)
    {
        if (flag == 0)
        {
            break;
        }
        sleep(1);

        // 开始选择任务
        // 本质上就是获取任务码
        int command = init.SelectTask();

        // 分配进程
        channel c = channels[position++];
        position %= channels.size();

        cout << "send command: " << command << " in " << c.GetName() << " by father:" << getpid() << endl;
        // 发送任务
        write(c.GetFd(), &command, sizeof(command));

        flag--;
    }
    cout << "任务已完成" << endl;
}
void ReleaseChannel(const vector<channel> &channels)
{
    for (const auto &e : channels)
    {
        // 关掉 父进程开辟的写端，注意这里的子进程
        close(e.GetFd());
        pid_t rid = waitpid(e.GetId(), nullptr, 0);
    }
}

int main()
{

    vector<channel> channels;
    // 创建管道
    CreatChannels(channels);
    // 发送任务并执行
    SendCommand(channels, 5);
    // 解决子进程回收问题
    ReleaseChannel(channels);
}
